"""
完整同步景点数据命令
1. 确保每个类别至少10条数据
2. 获取完整信息（包括多个图片）
3. 自动去重，只保留大范围景点
"""
from django.core.management.base import BaseCommand
from django.conf import settings
from django.core.cache import cache
from django.db.models import Count
from apps.attractions.models import Attraction
from utils.amap_client import AMapClient
from decimal import Decimal
import time
import re


class Command(BaseCommand):
    help = '完整同步景点数据，确保每个类别至少10条，获取完整信息'

    def add_arguments(self, parser):
        parser.add_argument(
            '--min-per-category',
            type=int,
            default=10,
            help='每个类别最少景点数量，默认10'
        )
        parser.add_argument(
            '--max-per-category',
            type=int,
            default=50,
            help='每个类别最多景点数量，默认50'
        )
        parser.add_argument(
            '--skip-deduplicate',
            action='store_true',
            help='跳过去重步骤'
        )

    def handle(self, *args, **options):
        min_per_category = options['min_per_category']
        max_per_category = options['max_per_category']
        skip_deduplicate = options['skip_deduplicate']
        
        self.stdout.write(self.style.SUCCESS('=' * 60))
        self.stdout.write(self.style.SUCCESS('开始完整同步景点数据'))
        self.stdout.write(self.style.SUCCESS('=' * 60))
        self.stdout.write(f'每个类别最少: {min_per_category} 个')
        self.stdout.write(f'每个类别最多: {max_per_category} 个')
        self.stdout.write('')

        # 检查API Key
        api_key = settings.AMAP_CONFIG.get('API_KEY')
        if not api_key or api_key == 'your_amap_api_key':
            self.stdout.write(self.style.ERROR('❌ 高德地图API Key未配置！'))
            return

        amap_client = AMapClient(api_key)

        # 定义各类别的搜索关键词和类型
        category_config = {
            'cultural': {
                'keywords': ['博物馆', '纪念馆', '古迹', '遗址', '古建筑', '文化园', '历史建筑', '文物', '古寺', '古塔'],
                'types': '风景名胜|文化设施|历史建筑',
                'name': '文化古迹'
            },
            'natural': {
                'keywords': ['公园', '风景区', '湖泊', '山', '湿地', '森林公园', '生态园', '植物园', '动物园', '水景'],
                'types': '风景名胜|公园广场|自然景观',
                'name': '自然风光'
            },
            'modern': {
                'keywords': ['博物馆', '科技馆', '展览馆', '艺术馆', '图书馆', '体育馆', '购物中心', '商业街', '广场', '地标'],
                'types': '科教文化服务|购物服务|体育休闲服务',
                'name': '现代建筑'
            },
            'food': {
                'keywords': ['美食街', '小吃街', '美食城', '夜市', '购物街', '商业街', '特产', '美食广场', '餐饮', '特色小吃'],
                'types': '餐饮服务|购物服务',
                'name': '美食购物'
            }
        }

        total_added = 0
        total_merged = 0

        # 处理每个类别
        for category, config in category_config.items():
            self.stdout.write(self.style.WARNING(f'\n处理类别: {config["name"]} ({category})'))
            
            # 检查当前数量
            current_count = Attraction.objects.filter(category=category).count()
            self.stdout.write(f'当前数量: {current_count} 个')
            
            if current_count >= min_per_category:
                self.stdout.write(self.style.SUCCESS(f'✓ 已达到最少数量要求'))
                continue
            
            needed = min_per_category - current_count
            self.stdout.write(f'需要添加: {needed} 个')
            
            # 获取已存在的POI ID和名称
            existing_poi_ids = set(
                Attraction.objects.exclude(amap_poi_id__isnull=True)
                .exclude(amap_poi_id='')
                .values_list('amap_poi_id', flat=True)
            )
            
            existing_names = set(
                Attraction.objects.filter(category=category)
                .values_list('name', flat=True)
            )
            
            added_count = 0
            processed_poi_ids = set()
            
            # 遍历关键词搜索
            for keyword in config['keywords']:
                if added_count >= needed:
                    break
                
                self.stdout.write(f'  搜索关键词: {keyword}')
                
                try:
                    page = 1
                    while added_count < needed and page <= 5:  # 最多5页
                        result = amap_client.search_pois_by_keyword(
                            keyword=keyword,
                            city='南昌市',
                            types=config['types'],
                            page=page,
                            page_size=20
                        )
                        
                        if not result or result.get('status') != '1':
                            break
                        
                        pois = result.get('pois', [])
                        if not pois:
                            break
                        
                        for poi in pois:
                            if added_count >= needed:
                                break
                            
                            poi_id = poi.get('id')
                            poi_name = poi.get('name', '').strip()
                            
                            # 检查是否已存在
                            if not poi_id or poi_id in existing_poi_ids or poi_id in processed_poi_ids:
                                continue
                            
                            # 检查名称是否重复
                            if poi_name in existing_names:
                                continue
                            
                            # 检查是否是子景点（如"XX-YY"格式）
                            if self.is_sub_attraction(poi_name, existing_names):
                                continue
                            
                            processed_poi_ids.add(poi_id)
                            
                            # 获取POI详情
                            try:
                                time.sleep(0.3)  # 避免请求过快
                                poi_detail = amap_client.get_poi_detail(poi_id)
                                
                                if not poi_detail or poi_detail.get('status') != '1':
                                    continue
                                
                                poi_data = poi_detail.get('pois', [{}])[0] if poi_detail.get('pois') else {}
                                
                                # 解析数据
                                location_str = poi.get('location', '') or poi_data.get('location', '')
                                if not location_str:
                                    continue
                                
                                lon, lat = location_str.split(',')
                                
                                # 获取图片（多个）
                                image_urls = self.extract_images(poi_data)
                                
                                # 解析其他信息
                                address = poi.get('address', '') or poi_data.get('address', '')
                                description = poi_data.get('introduction', '') or poi_data.get('content', '') or poi.get('intro', '')
                                
                                # 解析评分
                                rating = 4.0
                                rating_str = poi_data.get('rating', '') or poi.get('rating', '')
                                if rating_str:
                                    try:
                                        rating = float(rating_str)
                                    except:
                                        pass
                                
                                popularity = int(rating * 20) if rating else 50
                                
                                # 解析门票价格
                                ticket_price = Decimal('0')
                                cost = poi_data.get('cost', '')
                                if cost:
                                    price_match = re.search(r'(\d+\.?\d*)', cost)
                                    if price_match:
                                        try:
                                            ticket_price = Decimal(price_match.group(1))
                                        except:
                                            pass
                                
                                # 创建景点
                                attraction = Attraction.objects.create(
                                    name=poi_name,
                                    category=category,
                                    latitude=Decimal(lat),
                                    longitude=Decimal(lon),
                                    address=address,
                                    description=description,
                                    ticket_price=ticket_price,
                                    popularity=popularity,
                                    rating=rating,
                                    amap_poi_id=poi_id,
                                    amap_image_urls=image_urls
                                )
                                
                                added_count += 1
                                total_added += 1
                                existing_names.add(poi_name)
                                existing_poi_ids.add(poi_id)
                                
                                self.stdout.write(
                                    self.style.SUCCESS(f'    ✓ [{added_count}/{needed}] 添加: {poi_name} (图片: {len(image_urls)}张)')
                                )
                                
                            except Exception as e:
                                self.stdout.write(
                                    self.style.ERROR(f'    处理POI失败: {poi_name} - {str(e)}')
                                )
                                continue
                        
                        if len(pois) < 20:
                            break
                        
                        page += 1
                        time.sleep(0.5)
                    
                except Exception as e:
                    self.stdout.write(
                        self.style.ERROR(f'  搜索失败: {keyword} - {str(e)}')
                    )
                    continue
            
            self.stdout.write(
                self.style.SUCCESS(f'\n类别 {config["name"]} 共添加 {added_count} 个景点')
            )
        
        # 去重（如果未跳过）
        if not skip_deduplicate:
            self.stdout.write(self.style.WARNING('\n执行去重操作...'))
            merged_count = self.deduplicate_attractions()
            total_merged = merged_count
        
        # 清除缓存
        self.stdout.write(self.style.WARNING('\n清除景点数据缓存...'))
        try:
            if hasattr(cache, 'delete_pattern'):
                cache.delete_pattern('attractions_list_*')
                cache.delete_pattern('attraction_detail_*')
                cache.delete_pattern('attraction_recommendations_*')
            else:
                cache.clear()
            self.stdout.write(self.style.SUCCESS('✓ 已清除缓存'))
        except Exception as e:
            self.stdout.write(self.style.WARNING(f'清除缓存时出错: {e}'))
        
        # 最终统计
        self.stdout.write(self.style.SUCCESS('\n' + '=' * 60))
        self.stdout.write(self.style.SUCCESS('同步完成！'))
        self.stdout.write(self.style.SUCCESS('=' * 60))
        self.stdout.write(f'新增景点: {total_added} 个')
        self.stdout.write(f'合并重复: {total_merged} 个')
        self.stdout.write('')
        
        # 显示各类别数量
        self.stdout.write(self.style.WARNING('各类别最终数量:'))
        for category, config in category_config.items():
            count = Attraction.objects.filter(category=category).count()
            status = '✓' if count >= min_per_category else '✗'
            self.stdout.write(f'  {status} {config["name"]}: {count} 个')

    def extract_images(self, poi_data):
        """提取图片URL列表"""
        image_urls = []
        
        # 尝试多种字段名
        photos = poi_data.get('photos', [])
        if not photos:
            photos = poi_data.get('photo', [])
        if not photos:
            photos = poi_data.get('images', [])
        
        # 处理不同格式
        if isinstance(photos, str):
            try:
                import json
                photos = json.loads(photos)
            except:
                photos = []
        
        if isinstance(photos, list):
            for photo in photos[:10]:  # 最多10张图片
                if isinstance(photo, dict):
                    url = photo.get('url', '') or photo.get('src', '')
                    if url:
                        # 确保URL完整
                        if not url.startswith('http'):
                            url = 'https://restapi.amap.com' + url
                        image_urls.append(url)
                elif isinstance(photo, str) and photo.startswith('http'):
                    image_urls.append(photo)
        
        return image_urls

    def is_sub_attraction(self, name, existing_names):
        """检查是否是子景点"""
        # 检查是否是"XX-YY"格式
        pattern = re.compile(r'^(.+?)[-_（(].+')
        match = pattern.match(name)
        if match:
            parent_name = match.group(1).strip()
            if parent_name in existing_names:
                return True
        
        # 检查是否包含在其他名称中
        for existing_name in existing_names:
            if name in existing_name or existing_name in name:
                # 检查长度差异，如果差异很大，可能是子景点
                if abs(len(name) - len(existing_name)) > 5:
                    return True
        
        return False

    def deduplicate_attractions(self):
        """去重和合并"""
        from math import radians, cos, sin, asin, sqrt
        
        attractions = Attraction.objects.all().order_by('id')
        duplicates = []
        processed_ids = set()
        
        def calculate_distance(lat1, lon1, lat2, lon2):
            lat1, lon1, lat2, lon2 = map(radians, [float(lat1), float(lon1), float(lat2), float(lon2)])
            dlat = lat2 - lat1
            dlon = lon2 - lon1
            a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
            c = 2 * asin(sqrt(a))
            return c * 6371
        
        def name_similarity(name1, name2):
            def normalize(name):
                name = re.sub(r'[-_].*$', '', name)
                name = re.sub(r'\(.*?\)', '', name)
                name = re.sub(r'（.*?）', '', name)
                return name.strip()
            
            norm1 = normalize(name1)
            norm2 = normalize(name2)
            
            if norm1 == norm2:
                return 1.0
            if norm1 in norm2 or norm2 in norm1:
                return 0.8
            
            set1 = set(norm1)
            set2 = set(norm2)
            if len(set1) == 0 or len(set2) == 0:
                return 0.0
            
            intersection = len(set1 & set2)
            union = len(set1 | set2)
            return intersection / union if union > 0 else 0.0
        
        merged_count = 0
        
        for i, attr1 in enumerate(attractions):
            if attr1.id in processed_ids:
                continue
            
            similar_attrs = []
            for attr2 in attractions[i+1:]:
                if attr2.id in processed_ids:
                    continue
                
                similarity = name_similarity(attr1.name, attr2.name)
                if similarity >= 0.7:
                    distance = calculate_distance(
                        attr1.latitude, attr1.longitude,
                        attr2.latitude, attr2.longitude
                    )
                    if distance < 1.0:
                        similar_attrs.append((attr2, similarity, distance))
            
            if similar_attrs:
                candidates = [attr1] + [attr[0] for attr in similar_attrs]
                # 选择保留的景点：优先选择名称更短、更通用的（大范围）
                best = min(candidates, key=lambda a: (
                    len(a.name),  # 名称更短
                    -len(a.description or ''),  # 描述更长
                    -(a.rating or 0),  # 评分更高
                    -(a.popularity or 0)  # 热度更高
                ))
                
                to_merge = [a for a in candidates if a.id != best.id]
                if to_merge:
                    for attr in to_merge:
                        self.migrate_data(best, attr)
                        attr.delete()
                        merged_count += 1
                    processed_ids.update([a.id for a in to_merge])
                    processed_ids.add(best.id)
        
        return merged_count

    def migrate_data(self, keep, merge):
        """迁移数据"""
        # 迁移评价
        for review in merge.reviews.all():
            review.attraction = keep
            review.save()
        
        # 迁移收藏
        for favorite in merge.favorited_by.all():
            if not keep.favorited_by.filter(user=favorite.user).exists():
                favorite.attraction = keep
                favorite.save()
            else:
                favorite.delete()
        
        # 迁移图片
        for image in merge.images.all():
            image.attraction = keep
            image.save()
        
        # 合并高德地图图片
        if merge.amap_image_urls:
            existing_images = set(keep.amap_image_urls or [])
            new_images = set(merge.amap_image_urls)
            keep.amap_image_urls = list(existing_images | new_images)
        
        # 更新其他字段
        from django.db.models import Avg
        avg_rating = keep.reviews.aggregate(avg=Avg('rating'))['avg']
        if avg_rating:
            keep.rating = round(avg_rating, 2)
        
        keep.popularity = max(keep.popularity or 0, merge.popularity or 0)
        
        if not keep.description or len(keep.description) < len(merge.description or ''):
            keep.description = merge.description
        
        if not keep.address and merge.address:
            keep.address = merge.address
        
        if not keep.amap_poi_id and merge.amap_poi_id:
            keep.amap_poi_id = merge.amap_poi_id
        
        keep.save()

